/*
This source file is part of the open source TripleGasm project
Copyright (C) 2010 Roma Kovalev

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

For the latest info, see http://code.google.com/p/triplegasm
You can contact the author(s) via e-mail: endiannesz@gmail.com
*/

#pragma once
#ifndef MANIPULATOR_HPP
#define MANIPULATOR_HPP

#include <list>
#include <OISKeyboard.h>
#include <Ogre.h>
#include "basesphere.hpp"

namespace TripleGasm
{
  class Ball;

  /**The manipulator attaches spheres to itself.
  When initialized, the manipulator represents a sphere, unlike a sphere of the
  Ball class, this sphere does not move, it only rotates. Other spheres attach
  themselves to the manipulator, and become part of the manipulator.
  */
  class Manipulator : public BaseSphere
  {
  public:
    static const Ogre::Real MANIPULATOR_SPEED;

    /**Constructs a manipulator.
    @params
    Pointer to a scene manager that contains a "Sphere" mesh.
    */
    Manipulator(Ogre::SceneManager* pSm);
    Manipulator();
    ~Manipulator();

    /**Attempts to add a sphere, returns false if no sphere attached.
    This is the core function of TripleGasm, it attempts to attach a sphere,
    if there is no collision, it will return false, indicating that no sphere
    was added. If a sphere IS added, then its pointer is removed from the TripleGasm
    class, and the manipulator should delete the sphere when it is not used.
    */
    bool AddSphere(Ball* pSphere);

    /**Moves the sphere.
    @params
    A variable that contains the amount of time passed since last frame. 
    */
    void Move(const Ogre::FrameEvent& EVT);

    /**Sets the rotation speed.
    @note
    You should use this function only if user input indicates that the 
    manipulator should start rotating, or stop.
    */
    void SetSpeed(const Ogre::Real SPEED);

    const Ogre::Vector3 GetPosition() const;

    const Ogre::Vector3 GetDerivedPosition() const;

    /**Modifies an element in local_matrix.
    There is no need to pass the index of the element you want to set,
    the function gets it automatically with ->GetGraphPos().
    */
    void SetLocalMatrixElement(BaseSphere* pSphere);

    /**Returns an element in local_matrix adjusted by DIRECTION.
    The direction should be viewed in terms of a zero based matrix with
    the first index representing columns, second index representing rows.
    E.g. (pair::(5,5), direction::LEFT) would return pair::(4,5)*/
    BaseSphere* GetLocalMatrixElement(
      const std::pair<BYTE, BYTE>& POS,
      const direction DIRECTION= NONE);

    void ClearLocalMatrixElement(BaseSphere* pSphere);
    void ClearLocalMatrixElement(Ball* pSphere);

    /**Checks if sphere deletion is possible.
    If there are 3 or more spheres of the same colour linked together,
    they should be removed.
    @note
    This function should not be called right after a sphere is attached,
    because Ogre updates the SceneNodes only after the frame ends.
    */
    void TryDeleteSpheres();

#ifdef _DEBUG // This needs overriding only for the debug overlay, not needed for release
    void SetNodeData(BaseSphere* pSphere);
#endif

  private:
    local_matrix mLocalMatrix; /**<See #local_matrix.*/
    std::list<Ball*> mSpheres; /**<A list of all the sphere attached to the manipulator.*/
    Ogre::Real mMoveSpeed; /**<Rotation speed.*/

    // This is used for rotation, not related to gameplay.
    Ogre::SceneNode* mpOuterNode;
    Ogre::SceneNode* mpCenterNode;
    Ogre::SceneNode* mpInnerNode;

    Ogre::Entity* mpOuterEnt;
    Ogre::Entity* mpCenterEnt;
    /*Ogre::Entity* mpInnerEnt;*/ //Inner entity will be BaseSphere::mpEnt
  };
}

#endif